package conversion.utils.converter;


import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.SerializationFeature;

import java.io.*;

import com.opencsv.CSVReader;
import com.opencsv.exceptions.CsvException;

import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.apache.commons.text.StringEscapeUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;



/**
 * CsvProcessor 类用于处理CSV字符串，提供转换等操作。
 * @author ZJW
 * 版本：1.0
 */
public class CsvProcessor {


    /**
     * 将指定的CSV字符串转换为JSON字符串
     *
     * @param csvString 要转换的csv字符串
     * @return json字符串
     * @throws IOException  如果读取CSV字符串或写入JSON字符串时出现异常，抛出IOException。
     * @throws CsvException  如果CSV字符串格式不正确，抛出CsvException。
     * */
    public static String convertToJSON(String csvString) throws IOException, CsvException {

        // 初始化CSVReader对象并读取CSV字符串
        CSVReader csvReader = new CSVReader(new StringReader(csvString));
        List<String[]> rows = csvReader.readAll();
        csvReader.close();

        // 获取CSV文件中的列头
        String[] headers = rows.get(0);
        // 初始化List，用于存储每一行的数据
        List<Map<String, String>> dataList = new ArrayList<>();

        // 循环读取CSV文件中的每一行数据，将其添加到dataList中
        for (int i = 1; i < rows.size(); i++) {
            String[] rowData = rows.get(i);
            Map<String, String> dataMap = createDataMap(headers, rowData);
            dataList.add(dataMap);
        }

        // 初始化ObjectMapper对象
        ObjectMapper objectMapper = new ObjectMapper();
        // 启用美化输出
        objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
        // 使用默认的美化格式
        ObjectWriter writer = objectMapper.writerWithDefaultPrettyPrinter();

        // 将Java对象转换成JSON字符串
        return writer.writeValueAsString(dataList);

    }


    /**
     *每一行的数据转换为一个Map<String, String>类型的变量dataMap
     *
     * @param headers  列头
     * @param rowData  该行数据
     * @return  遍历每一列的数据，将其添加到dataMap中，并返回这个Map变量
     */
    private static Map<String, String> createDataMap(String[] headers, String[] rowData) {
        // 初始化Map用于存储每一列的数据
        Map<String, String> dataMap = new LinkedHashMap<>();
        // 循环遍历每一列的数据，将数据添加到dataMap中
        for (int i = 0; i < headers.length; i++) {
            String header = headers[i];
            String value = rowData[i];
            dataMap.put(header, value);
        }
        return dataMap;
    }

    /**
     * 读取指定的CSV字符串并将其作为行的数组列表返回
     *
     * @param csvString 要读取的CSV字符串
     * @return CSV文件的行列表
     * @throws IOException 当BufferedReader读取失败时抛异常
     */
    private static List<String[]> readCsv(String csvString) throws IOException {
        List<String[]> rows = new ArrayList<>();
        // 正则表达式匹配逗号前后是否有双引号，并且双引号之间没有其他双引号
        String regex = ",(?=([^\"]*\"[^\"]*\")*[^\"]*$)";
        Pattern pattern = Pattern.compile(regex);

        // 使用Try-with-resources以确保正确关闭
        try (BufferedReader br = new BufferedReader(new StringReader(csvString))) {
            String line;
            while ((line = br.readLine()) != null) {
                rows.add(pattern.split(line));
            }
        }

        return rows;
    }

    /**
     * 将指定的CSV字符串转换为YAML字符串
     *
     * @param csvString 要转换的CSV字符串
     * @return YAML格式的字符串
     */
    public static String convertToYAML(String csvString) throws IOException, CsvException {
        // 初始化CSVReader对象并读取CSV文件

        CSVReader csvReader = new CSVReader(new StringReader(csvString));
        List<String[]> rows = csvReader.readAll();
        csvReader.close();

        // 初始化用于存储列头和数据的List
        List<String> headers = new ArrayList<>();
        List<Map<String, String>> data = new ArrayList<>();

        // 提取列头
        if (!rows.isEmpty()) {
            // 如果CSV文件不为空，则提取第一行作为列头
            String[] headerRow = rows.get(0);
            for (String header : headerRow) {
                // 将列头添加到headers List中，并去掉字符串两端的空格
                headers.add(header.trim());
            }
        }

        // 提取数据行
        for (int i = 1; i < rows.size(); i++) {
            // 从第二行开始循环，提取每一行的数据
            String[] rowData = rows.get(i);
            Map<String, String> rowMap = new LinkedHashMap<>();
            for (int j = 0; j < headers.size() && j < rowData.length; j++) {
                // 将每一行的数据和列头对应，并去掉字符串两端的空格
                String value = rowData[j].trim();
                rowMap.put(headers.get(j), value);
            }
            // 将处理完的每一行数据添加到data List中
            data.add(rowMap);

        }

        // 创建一个DumperOptions对象
        DumperOptions options = new DumperOptions();

        // 设置将YAML文件以块状形式进行缩进（Default: false）

        options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);

        // 创建一个YAML对象并设置配置
        Yaml yaml = new Yaml(options);

        return yaml.dump(data);

    }


    /**
     * 验证给定的CSV字符串是否符合预期的格式
     *
     * @param csvString 要验证的CSV字符串
     * @return 如果CSV数据格式有效，则为true；否则为false
     */
    public static boolean isFormatValid(String csvString) {
        boolean isValid = true;
        int expectedNumberOfColumns = -1;

        try  {
            //读取读取CSV文件中的所有行
            List<String[]> rows = readCsv(csvString);

            // 检查行数
            if (rows.size() < 2) {
                return false;
            }

            // 删除CSV文件的头部并获取列标题
            String[] csvHeaders = rows.remove(0);

//            设置预期的列数
            expectedNumberOfColumns = csvHeaders.length;

            for (String[] row : rows) {

                if (row.length != expectedNumberOfColumns){
                    isValid = false;
                    break;
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
            isValid = false;
        }
        return isValid;
    }


    /**
     * 将CSV格式的字符串转换为XML格式的字符串
     *
     * @param csvString CSV格式的字符串
     * @return XML格式的字符串
     * @throws Exception 转换过程中发生的异常
     */
    public static String convertToXML(String csvString) throws Exception {
        // 创建一个文档工厂实例
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        // 创建一个文档构建器
        DocumentBuilder builder = factory.newDocumentBuilder();
        // 创建一个新文档
        Document doc = builder.newDocument();

        // 设置CSV格式，空字符串将被解析为null
        CSVFormat format = CSVFormat.DEFAULT.withNullString("");

        // 解析CSV字符串并返回CSVParser对象
        CSVParser parser = CSVParser.parse(new StringReader(csvString), format);

        // 创建根元素节点
        Element rootElement = doc.createElement("data");
        doc.appendChild(rootElement);

        // 是否是第一行（列标题）
        boolean firstRow = true;
        // 存储列头信息的列表
        List<String> headers = new ArrayList<>();

        // 遍历CSVParser对象中的每个记录
        for (CSVRecord record : parser) {
            // 如果是第一行，解析列标题信息并跳过该行
            if (firstRow) {
                for (String header : record) {
                    XmlProcessor.processNodeName(header);
                    headers.add(header);
                }
                firstRow = false;
                continue;
            }

            // 创建每行数据的元素节点
            Element rowElement = doc.createElement("row");
            rootElement.appendChild(rowElement);

            // 遍历每个记录中的字段
            for (int i = 0; i < record.size(); i++) {
                // 创建字段的元素节点，使用列标题作为节点名称
                String escapedHeader = StringEscapeUtils.escapeXml11(headers.get(i));

                Element fieldElement = doc.createElement(XmlProcessor.processNodeName(escapedHeader));
                String value = record.get(i);
                // 使用StringEscapeUtils.escapeXml11()方法转义特殊字符
                value = StringEscapeUtils.escapeXml11(value);
                // 设置字段元素节点的文本内容为字段值
                fieldElement.setTextContent(value);
                rowElement.appendChild(fieldElement);
            }
        }

        // 创建一个转换器工厂实例
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        // 创建一个转换器
        Transformer transformer = transformerFactory.newTransformer();
        // 设置转换器的输出属性
        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        // 创建一个DOM源，将文档与转换器连接起来
        DOMSource source = new DOMSource(doc);
        StringWriter writer = new StringWriter();
        // 创建一个结果流，用于接收转换后的XML数据
        StreamResult result = new StreamResult(writer);
        // 将源文档转换为目标XML文档
        transformer.transform(source, result);

        // 将结果转换为字符串并返回
        return writer.toString();
    }

}


